import numpy as np
import cv2,os
import json
import matplotlib.pyplot as plt
from .utils_weice import *

def show(img,img_name):
    cv2.imshow(img_name,img)
    cv2.waitKey(0)
    #cv2.destroyAllWindows()

def getMask(file_path):
    with open(file_path, 'r') as f:
        label_data = json.load(f)

    label_data_list = label_data['content']
    img_list = []
    mask_list = []
    for i in range(len(label_data_list)):
        img_list.append(label_data_list[i]['filename'])
        mask_list.append(label_data_list[i]['zhenhen_crop'])
    return img_list,mask_list


def filter_edges_by_connectivity(edges, connectivity=8,min_line_length=20):
    ret,labels = cv2.connectedComponents(edges, connectivity=connectivity)  # connectivity表示连通域的连通类型，可选4或8，默认为8，上下左右和对角线八个方向
    unique_labels = np.unique(labels)
    component_sizes = [np.sum(labels == label) for label in unique_labels]

    min_size = min_line_length
    filtered_labels = [label for label, size in zip(unique_labels, component_sizes) if size >= min_size]

    conn = np.isin(labels, filtered_labels)
    edges_tobe_filtered = np.where(conn, 255, 0).astype(np.uint8)
    edges = cv2.bitwise_and(edges, edges_tobe_filtered)
    return edges


def refine_instance_boundary(img, point_list,box):
    # 将point_list转为numpy数组
    point_list_np = np.array(point_list, dtype=np.int32)

    # 创建一个与img大小相同的全0图像mask
    mask = np.zeros_like(img, dtype=np.uint8)
    mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)

    # 使用填充颜色在mask上画多边形
    cv2.fillPoly(mask, [point_list_np], color=(255))

    # 截取box区域
    box,_ = check_bbox_bounds(img,box)
    x_min, y_min, x_max, y_max = box
    img_roi = img[y_min:y_max, x_min:x_max].copy()
    mask_roi = mask[y_min:y_max, x_min:x_max].copy()

    # 1！
    # 去噪以及边缘提取
    gray = cv2.cvtColor(img_roi, cv2.COLOR_BGR2GRAY)
    # 双边滤波 去噪，保留边缘信息
    gray = cv2.bilateralFilter(gray, 9, 75, 75)
    # 高斯模糊，去噪
    blurred = cv2.GaussianBlur(gray, (5, 5), 0) #(3,3),(7,7)效果不佳
    low_threshold = 100
    high_threshold = 130#120
    edges = cv2.Canny(blurred, low_threshold, high_threshold)
    # sobel算子
    #edges = cv2.Sobel(blurred, cv2.CV_8U, 1, 0, ksize=3)
    # laplacian算子
    #edges = cv2.Laplacian(blurred, cv2.CV_8U, ksize=3)


    # 2！
    # 连通性分析/霍夫直线检测
    conn = 8
    min_line_len = 30
    edges = filter_edges_by_connectivity(edges, connectivity=conn,min_line_length=min_line_len)

    # 3！
    # 先膨胀后腐蚀，闭运算
    # 先腐蚀后膨胀，开运算
    ke = (3,3)
    kernel = np.ones(ke, np.uint8)
    
    # edges = cv2.dilate(edges, kernel, iterations=iter) # 膨胀
    # edges = cv2.erode(edges, kernel, iterations=iter)  # 腐蚀
    edges = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel) 

    # 4！
    # 对edge提取最大外围轮廓
    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if len(contours) == 0:
        point_list = [[point] for point in point_list]
        return point_list
    max_cnt = max(contours, key=cv2.contourArea)
    max_cnt = np.array(max_cnt)

    # 5！
    # 得到max_cnt构成mask和mask_roi的并集
    mask_p1 = mask_roi.copy()
    mask_zero = np.zeros_like(mask_roi, dtype=np.uint8)
    mask_p2 = cv2.drawContours(mask_zero, [max_cnt], -1, 255, cv2.FILLED)
    mask_or = cv2.bitwise_or(mask_p1,mask_p2)

    # 得到max_cnt构成mask和mask_roi的差集
    mask_p1 = mask_roi.copy()
    mask_zero = np.zeros_like(mask_roi, dtype=np.uint8)
    mask_p2 = cv2.drawContours(mask_zero, [max_cnt], -1, 255, cv2.FILLED)
    mask_diff = cv2.bitwise_xor(mask_p1,mask_p2)

    '''
    # 得到max_cnt构成mask和mask_roi的交集
    mask_p1 = mask_roi.copy()
    mask_zero = np.zeros_like(mask_roi, dtype=np.uint8)
    mask_p2 = cv2.drawContours(mask_zero, [np.array(max_cnt)], -1, (255,255,255), -1)
    mask_and = cv2.bitwise_and(mask_p1,mask_p2)
    '''
    mask_and = cv2.bitwise_xor(mask_or,mask_diff)

    # 统计img_roi中对应mask_and前景像素点的平均RGB值
    # img_roi_fg = cv2.bitwise_and(img_roi,img_roi,mask=cv2.cvtColor(mask_and,cv2.COLOR_BGR2GRAY))
    img_roi_fg = cv2.bitwise_and(img_roi,img_roi,mask=mask_and)
    mean_val = cv2.mean(img_roi_fg,mask=mask_and)[:-1]

    # 统计img_roi中mask_or背景像素点的平均RGB值
    mask_bg = cv2.bitwise_not(mask_or)
    mean_val_bg = cv2.mean(img_roi,mask=mask_bg)[:-1]

    # 判断img_roi中对应mask_diff前景像素点的每个像素点的灰度值，更加接近于背景还是前景
    img_roi_fg = cv2.bitwise_and(img_roi,img_roi,mask=mask_diff)
    '''
    # 如果这个像素点接近背景，则在mask_or中将这个像素点置为0，否则保留
    for i in range(img_roi_fg.shape[0]):
        for j in range(img_roi_fg.shape[1]):
            dist1 = np.linalg.norm(img_roi_fg[i,j]-mean_val)    # 与前景针痕的距离
            dist2 = np.linalg.norm(img_roi_fg[i,j]-mean_val_bg) # 与背景的距离
            #print(f"dist1:{dist1},dist2:{dist2}") if dist1 > dist2 else print(f"",end="")
            if dist1 > dist2:
                mask_or[i,j] = 0
    '''
    dist1_matrix = np.linalg.norm(img_roi_fg - mean_val, axis=-1)
    dist2_matrix = np.linalg.norm(img_roi_fg - mean_val_bg, axis=-1)
    
    mask_or[dist1_matrix > dist2_matrix] = 0
    
    # 提取mask_or的最大外围轮廓
    contours, _ = cv2.findContours(mask_or, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if len(contours) == 0:
        point_list = [[point] for point in point_list]
        return point_list
    max_cnt = max(contours, key=cv2.contourArea)
    # print("max_cnt",max_cnt)
    #求最大外接矩阵
    # x, y, w, h = cv2.boundingRect(max_cnt)
    # print('x,y,w,h:',x,y,w,h)
    # box_rect = [x+x_min,y+y_min,x+w+x_min,y+h+y_min]
    # print('box_rect',box_rect)
    # edges = np.zeros_like(edges)
    # edges = cv2.drawContours(edges, [max_cnt], -1, 255, 1)
    

    # 把坐标还原至原图中
    new_point_list = [[point[0].tolist()] for point in max_cnt + np.array([[x_min, y_min]], dtype=np.int32)]
    return new_point_list


def pmi_refine_profile(image, new_point_list):
    # 将point_list转为numpy数组
    point_list_np = np.array(new_point_list, dtype=np.int32)

    # 创建一个与img大小相同的全0图像mask
    mask = np.zeros_like(image, dtype=np.uint8)
    mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)

    # 使用填充颜色在mask上画多边形
    cv2.fillPoly(mask, [point_list_np], color=(255))

    # 用开运算修剪毛边
    kernel = np.ones((5,5), np.uint8)
    edges = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)

    # edges = cv2.erode(mask, kernel, iterations=1)  # 腐蚀

    # 得到轮廓
    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if len(contours) == 0:
        # point_list = [[point] for point in new_point_list]
        return new_point_list

    new_point_list = contours[0].tolist()


    return new_point_list

def correct_per_img(image,crop_pic_list):
    boxes_list = []
    new_point_list = []
    nmpad_area = 0
    perimeter_sum = 0
    # box=[0,0,0,0]
    correct_point_list_list = []
    nmpad_area_list = []
    perimeter_sum_list = []
    for crop_pic in crop_pic_list:
        point_list = [[int(cor[0][0]),int(cor[0][1])] for cor in crop_pic]
        if len(point_list) < 5:
            continue

        # 得到外接矩形
        x,y,w,h = cv2.boundingRect(np.array(point_list))
        if [x,y,w,h] not in boxes_list:
            boxes_list.append([x,y,w,h])
        else:
            #print('重复的外接矩形,跳过')
            continue
    
        # 对外接矩形扩大5个像素
        x,y,w,h = x-5,y-5,w+10,h+10
        if(x<0):
            x = 0
        if(y<0):
            y = 0
        if(x+w>image.shape[1]):
            w = image.shape[1]-x
        if(y+h>image.shape[0]):
            h = image.shape[0]-y
        # 最终修正的轮廓
        new_point_list = refine_instance_boundary(image,point_list,[x,y,x+w,y+h])
        # correct_point_list_list.append(new_point_list)


        # 8.31更新 用修正过的mask_or的最大外围轮廓
        mask = np.zeros_like(image, dtype=np.uint8)
        mask = cv2.drawContours(mask, [np.array(new_point_list)], -1, 255, -1)
        
        gray_mask = cv2.cvtColor(mask,cv2.COLOR_BGR2GRAY)
        contours, _ = cv2.findContours(gray_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        if contours:
            max_contour = max(contours, key=cv2.contourArea)
            box_x, box_y, box_w, box_h = cv2.boundingRect(max_contour)
            box = [box_x,box_y,box_x+box_w,box_y+box_h]
            for contour in contours:
                perimeter = cv2.arcLength(contour, True)
                perimeter_sum += perimeter
                
        # print(box)
        correct_mask_roi = mask[y:y+h,x:x+w]
        # 计算mask非0像素点个数，即针痕像素点个数
        # print(x,y,w,h)
        # print('correct_mask_roi.shape:',correct_mask_roi.shape)
        nmpad_area = cv2.countNonZero(cv2.cvtColor(correct_mask_roi,cv2.COLOR_BGR2GRAY))
        # new_point_list = [new_point_list]
        
        # nmpad_area_list.append(nmpad_area)
        correct_point_list_list.append(new_point_list)
        nmpad_area_list.append(nmpad_area)
        perimeter_sum_list.append(perimeter_sum)
    #return new_point_list,nmpad_area,perimeter_sum
    return correct_point_list_list,np.sum(nmpad_area_list),np.sum(perimeter_sum_list)


def correct_per_img_pmi(image,crop_pic_list):
    boxes_list = []
    new_point_list = []
    nmpad_area = 0
    perimeter_sum = 0
    # box=[0,0,0,0]
    correct_point_list_list = []
    nmpad_area_list = []
    perimeter_sum_list = []
    for crop_pic in crop_pic_list:
        point_list = [[int(cor[0][0]),int(cor[0][1])] for cor in crop_pic]
        if len(point_list) < 5:
            continue

        # 得到外接矩形
        x,y,w,h = cv2.boundingRect(np.array(point_list))
        if [x,y,w,h] not in boxes_list:
            boxes_list.append([x,y,w,h])
        else:
            #print('重复的外接矩形,跳过')
            continue
    
        # 对外接矩形扩大5个像素
        x,y,w,h = x-5,y-5,w+10,h+10
        if(x<0):
            x = 0
        if(y<0):
            y = 0
        if(x+w>image.shape[1]):
            w = image.shape[1]-x
        if(y+h>image.shape[0]):
            h = image.shape[0]-y
        # 最终修正的轮廓
        new_point_list = refine_instance_boundary(image,point_list,[x,y,x+w,y+h])
        # correct_point_list_list.append(new_point_list)

        # 针对pmi针痕轮廓被背景影响的问题，再次进行轮廓修正
        new_point_list = pmi_refine_profile(image, new_point_list)
        new_point_list = pmi_refine_profile(image, new_point_list)

        # 8.31更新 用修正过的mask_or的最大外围轮廓
        mask = np.zeros_like(image, dtype=np.uint8)
        mask = cv2.drawContours(mask, [np.array(new_point_list)], -1, 255, -1)
        
        gray_mask = cv2.cvtColor(mask,cv2.COLOR_BGR2GRAY)
        contours, _ = cv2.findContours(gray_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        if contours:
            max_contour = max(contours, key=cv2.contourArea)
            box_x, box_y, box_w, box_h = cv2.boundingRect(max_contour)
            box = [box_x,box_y,box_x+box_w,box_y+box_h]
            for contour in contours:
                perimeter = cv2.arcLength(contour, True)
                perimeter_sum += perimeter
                
        # print(box)
        correct_mask_roi = mask[y:y+h,x:x+w]
        # 计算mask非0像素点个数，即针痕像素点个数
        # print(x,y,w,h)
        # print('correct_mask_roi.shape:',correct_mask_roi.shape)
        nmpad_area = cv2.countNonZero(cv2.cvtColor(correct_mask_roi,cv2.COLOR_BGR2GRAY))
        # new_point_list = [new_point_list]
        
        # nmpad_area_list.append(nmpad_area)
        correct_point_list_list.append(new_point_list)
        nmpad_area_list.append(nmpad_area)
        perimeter_sum_list.append(perimeter_sum)
    #return new_point_list,nmpad_area,perimeter_sum
    return correct_point_list_list,np.sum(nmpad_area_list),np.sum(perimeter_sum_list)

